<!DOCTYPE HTML>
<html lang="zh-Hans" class="sidebar-visible no-js light">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>译：Learn Rust the Dangerous Way 1 - Rust 优秀博文</title>


        <!-- Custom HTML head -->
        
        <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff" />

        <link rel="icon" href="../../favicon.svg">
        <link rel="shortcut icon" href="../../favicon.png">
        <link rel="stylesheet" href="../../css/variables.css">
        <link rel="stylesheet" href="../../css/general.css">
        <link rel="stylesheet" href="../../css/chrome.css">
        <link rel="stylesheet" href="../../css/print.css" media="print">

        <!-- Fonts -->
        <link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
        <link rel="stylesheet" href="../../fonts/fonts.css">

        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" href="../../highlight.css">
        <link rel="stylesheet" href="../../tomorrow-night.css">
        <link rel="stylesheet" href="../../ayu-highlight.css">

        <!-- Custom theme stylesheets -->

    </head>
    <body>
        <!-- Provide site root to javascript -->
        <script type="text/javascript">
            var path_to_root = "../../";
            var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
        </script>

        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script type="text/javascript">
            try {
                var theme = localStorage.getItem('mdbook-theme');
                var sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script type="text/javascript">
            var theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            var html = document.querySelector('html');
            html.classList.remove('no-js')
            html.classList.remove('light')
            html.classList.add(theme);
            html.classList.add('js');
        </script>

        <!-- Hide / unhide sidebar before it is displayed -->
        <script type="text/javascript">
            var html = document.querySelector('html');
            var sidebar = 'hidden';
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            }
            html.classList.remove('sidebar-visible');
            html.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <div class="sidebar-scrollbox">
                <ol class="chapter"><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">1.</strong> 类型系统</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../01_类型系统/Rust_Concept_Clarification_Deref_vs_AsRef_vs_Borrow_vs_Cow/Rust_Concept_Clarification_Deref_vs_AsRef_vs_Borrow_vs_Cow.html"><strong aria-hidden="true">1.1.</strong> Rust Concept Clarification Deref vs AsRef vs Borrow vs Cow</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust_Concept_Clarification_Deref_vs_AsRef_vs_Borrow_vs_Cow/Deref_AsRef_Borrow_Cow释义.html"><strong aria-hidden="true">1.2.</strong> Deref AsRef Borrow Cow 释义</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust的Borrow和AsRef：让你的代码用起来像呼吸一样自然/Rust的Borrow和AsRef：让你的代码用起来像呼吸一样自然.html"><strong aria-hidden="true">1.3.</strong> Rust的Borrow和AsRef：让你的代码用起来像呼吸一样自然</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust的Cow类型有什么用？详解Cow及其用途/Rust的Cow类型有什么用？详解Cow及其用途.html"><strong aria-hidden="true">1.4.</strong> Rust的Cow类型有什么用？详解Cow及其用途</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/判别Fn、FnMut、FnOnce的标准/判别Fn、FnMut、FnOnce的标准.html"><strong aria-hidden="true">1.5.</strong> 判别Fn、FnMut、FnOnce的标准</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/一行代码告诉你内部可变性的真相(UnsafeCell)/一行代码告诉你内部可变性的真相(UnsafeCell).html"><strong aria-hidden="true">1.6.</strong> 一行代码告诉你内部可变性的真相(UnsafeCell)</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Tour_of_Rust's_Standard_Library_Traits/Tour_of_Rust's_Standard_Library_Traits.html"><strong aria-hidden="true">1.7.</strong> Tour of Rust's Standard Library Traits</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/逆变、协变与子类型，以及Rust/逆变、协变与子类型，以及Rust.html"><strong aria-hidden="true">1.8.</strong> 逆变、协变与子类型，以及Rust</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust自引用结构、Pin与Unpin/Rust自引用结构、Pin与Unpin.html"><strong aria-hidden="true">1.9.</strong> Rust自引用结构、Pin与Unpin</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/为什么Rust需要Pin,Unpin/为什么Rust需要Pin,Unpin.html"><strong aria-hidden="true">1.10.</strong> 译：为什么 Rust 需要 Pin, Unpin ？</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/定海神针Pin和Unpin/定海神针Pin和Unpin.html"><strong aria-hidden="true">1.11.</strong> 译：定海神针 Pin 和 Unpin</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/sizedness-in-rust/sizedness-in-rust.html"><strong aria-hidden="true">1.12.</strong> Sizedness in Rust</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust生命周期集大成者PhantomData〈T〉/Rust生命周期集大成者PhantomData〈T〉.html"><strong aria-hidden="true">1.13.</strong> Rust生命周期集大成者 PhantomData&lt;T&gt;</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：用Rust做类型体操_Part_0.html"><strong aria-hidden="true">1.14.</strong> 数据库表达式执行的黑魔法：用Rust做类型体操 Part 0</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：GAT实现引用类型关联_Part_1.html"><strong aria-hidden="true">1.15.</strong> 数据库表达式执行的黑魔法：GAT实现引用类型关联 Part 1</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：用HRTB写bound_Part_2.html"><strong aria-hidden="true">1.16.</strong> 数据库表达式执行的黑魔法：用HRTB写bound Part 2</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：用Rust做类型体操之用宏展开重复代码_Part_3_&_4.html"><strong aria-hidden="true">1.17.</strong> 数据库表达式执行的黑魔法：用Rust做类型体操之用宏展开重复代码 Part 3 & 4</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：与Rust编译器斗智斗勇之表达式向量化_Part_5_&_6.html"><strong aria-hidden="true">1.18.</strong> 数据库表达式执行的黑魔法：与Rust编译器斗智斗勇之表达式向量化 Part 5 & 6</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：在Rust中用宏关联逻辑类型和实际类型_Part_7.html"><strong aria-hidden="true">1.19.</strong> 数据库表达式执行的黑魔法：在Rust中用宏关联逻辑类型和实际类型 Part 7</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">2.</strong> 生命周期</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../02_生命周期/Rust中的生命周期——从StrSplit实例说开去/Rust中的生命周期——从StrSplit实例说开去.html"><strong aria-hidden="true">2.1.</strong> Rust中的生命周期——从StrSplit实例说开去</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/与ChatGPT深度对话来学Rust生命周期/与ChatGPT深度对话来学Rust生命周期.html"><strong aria-hidden="true">2.2.</strong> 与ChatGPT深度对话来学Rust生命周期</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/进击的Rust生命周期——early_bound与late_bound（1）/进击的Rust生命周期——early_bound与late_bound（1）.html"><strong aria-hidden="true">2.3.</strong> 进击的Rust生命周期——early_bound与late_bound（1）</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/进击的Rust生命周期——early_bound与late_bound（2）/进击的Rust生命周期——early_bound与late_bound（2）.html"><strong aria-hidden="true">2.4.</strong> 进击的Rust生命周期——early_bound与late_bound（2）</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/进击的Rust生命周期——一力降十会的MIR（1）/进击的Rust生命周期——一力降十会的MIR（1）.html"><strong aria-hidden="true">2.5.</strong> 进击的Rust生命周期——一力降十会的MIR（1）</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/进击的Rust生命周期——一力降十会的MIR（2）/进击的Rust生命周期——一力降十会的MIR（2）.html"><strong aria-hidden="true">2.6.</strong> 进击的Rust生命周期——一力降十会的MIR（2）</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/Common_Rust_Lifetime_Misconceptions/Common_Rust_Lifetime_Misconceptions.html"><strong aria-hidden="true">2.7.</strong> Common Rust Lifetime Misconceptions</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/Rust生命周期常见误区/Rust生命周期常见误区.html"><strong aria-hidden="true">2.8.</strong> 译：Rust生命周期常见误区</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">3.</strong> 无畏并发</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../05_无畏并发/简单写个Rust无锁队列/简单写个Rust无锁队列.html"><strong aria-hidden="true">3.1.</strong> 简单写个Rust无锁队列</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/进击的Rust多线程——混合自旋锁/进击的Rust多线程——混合自旋锁.html"><strong aria-hidden="true">3.2.</strong> 进击的Rust多线程——混合自旋锁</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/An_unsafe_tour_of_Rust's_Send_and_Sync/An_unsafe_tour_of_Rust's_Send_and_Sync.html"><strong aria-hidden="true">3.3.</strong> An unsafe tour of Rust's Send and Sync</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/进击的Rust多线程——Send与Sync/进击的Rust多线程——Send与Sync.html"><strong aria-hidden="true">3.4.</strong> 进击的Rust多线程——Send与Sync</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/进击的Rust多线程——离经叛道的PhantomData/进击的Rust多线程——离经叛道的PhantomData.html"><strong aria-hidden="true">3.5.</strong> 进击的Rust多线程——离经叛道的PhantomData</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/Rust_Async_Pin概念解析/Rust_Async_Pin概念解析.html"><strong aria-hidden="true">3.6.</strong> Rust Async: Pin概念解析</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/Rust和C++的并发库对比/Rust和C++的并发库对比.html"><strong aria-hidden="true">3.7.</strong> 译：Rust 和 C++ 的并发库对比</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/Rust原子类型和内存排序/Rust原子类型和内存排序.html"><strong aria-hidden="true">3.8.</strong> Rust原子类型和内存排序</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">4.</strong> 网络编程</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../06_网络编程/从编解码层面理解WebSocket_手写一个WebSocket/从编解码层面理解WebSocket_手写一个WebSocket.html"><strong aria-hidden="true">4.1.</strong> 从编解码层面理解WebSocket 手写一 个WebSocket</a></li><li class="chapter-item expanded "><a href="../../06_网络编程/透过Rust探索系统的本原：网络篇/透过Rust探索系统的本原：网络篇.html"><strong aria-hidden="true">4.2.</strong> 透过Rust探索系统的本原：网络篇</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">5.</strong> 轮子系列</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../07_轮子系列/700行Rust写一个内存分配器/700行Rust写一个内存分配器.html"><strong aria-hidden="true">5.1.</strong> 700行Rust写一个内存分配器</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/Rust：网络库的实现思路/Rust：网络库的实现思路.html"><strong aria-hidden="true">5.2.</strong> Rust：网络库的实现思路</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/Rust异步运行时基础部件/Rust异步运行时基础部件.html"><strong aria-hidden="true">5.3.</strong> Rust异步运行时基础部件</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/使用Rust+epoll编写异步IO框架/使用Rust+epoll编写异步IO框架（1）.html"><strong aria-hidden="true">5.4.</strong> 使用Rust+epoll编写异步IO框架（1）</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/使用Rust+epoll编写异步IO框架/使用Rust+epoll编写异步IO框架（2）.html"><strong aria-hidden="true">5.5.</strong> 使用Rust+epoll编写异步IO框架（2）</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/使用Rust+epoll编写异步IO框架/使用Rust+epoll编写异步IO框架（3）.html"><strong aria-hidden="true">5.6.</strong> 使用Rust+epoll编写异步IO框架（3）</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day1.html"><strong aria-hidden="true">5.7.</strong> 用rust从零开发一套web框架：day1</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day2.html"><strong aria-hidden="true">5.8.</strong> 用rust从零开发一套web框架：day2</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day3.html"><strong aria-hidden="true">5.9.</strong> 用rust从零开发一套web框架：day3</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day4.html"><strong aria-hidden="true">5.10.</strong> 用rust从零开发一套web框架：day4</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day5.html"><strong aria-hidden="true">5.11.</strong> 用rust从零开发一套web框架：day5</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">6.</strong> 奇技淫巧</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../08_奇技淫巧/Copy-On-Write是不是优化？/Copy-On-Write是不是优化？.html"><strong aria-hidden="true">6.1.</strong> 译：Copy-On-Write是不是优化？</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/揭秘神奇的Rust_Axum风格的函数实现/揭秘神奇的Rust_Axum风格的函数实现.html"><strong aria-hidden="true">6.2.</strong> 译：揭秘神奇的 Rust Axum 风格的函数实现</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/“变长参数”函数与回调/“变长参数”函数与回调.html"><strong aria-hidden="true">6.3.</strong> “变长参数”函数与回调</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/Rust字符串格式化的幕后：format_args!()/Rust字符串格式化的幕后：format_args!().html"><strong aria-hidden="true">6.4.</strong> 译：Rust字符串格式化的幕后：format_args!()</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/给Rust带来一点C++特产/给Rust带来一点C++特产.html"><strong aria-hidden="true">6.5.</strong> 给Rust带来一点C++特产</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/一步步实现_Rust_Bevy_ECS_的_System_简化版本/一步步实现_Rust_Bevy_ECS_的_System_简化版本.html"><strong aria-hidden="true">6.6.</strong> 一步步实现 Rust Bevy ECS 的 System 简化版本</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/Exploring_Design_Patterns_in_Rust_with_Algorithmic_Trading_Examples/Exploring_Design_Patterns_in_Rust_with_Algorithmic_Trading_Examples.html"><strong aria-hidden="true">6.7.</strong> Exploring Design Patterns in Rust with Algorithmic Trading Examples</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">7.</strong> 源码分析</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../09_源码分析/Rust并发：bytes源码分析/Rust并发：bytes源码分析.html"><strong aria-hidden="true">7.1.</strong> Rust并发：bytes源码分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust并发：标准库Arc源码分析/Rust并发：标准库Arc源码分析.html"><strong aria-hidden="true">7.2.</strong> Rust并发：标准库Arc源码分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust并发：标准库sync_Once源码分析/Rust并发：标准库sync_Once源码分析.html"><strong aria-hidden="true">7.3.</strong> Rust并发：标准库sync::Once源码分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust源码阅读：引用计数Rc/Rust源码阅读：引用计数Rc.html"><strong aria-hidden="true">7.4.</strong> Rust源码阅读：引用计数Rc</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust源码阅读：Cell、RefCell与内部可变性/Rust源码阅读：Cell、RefCell与内部可变性.html"><strong aria-hidden="true">7.5.</strong> Rust源码阅读： Cell、RefCell与内部可变性</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/关于_Rust_的_UnsafeCell、Cell_与_RefCell/关于_Rust_的_UnsafeCell、Cell_与_RefCell.html"><strong aria-hidden="true">7.6.</strong> 关于 Rust 的 UnsafeCell、Cell 与 RefCell</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust_Async_async-stream源码分析/Rust_Async_async-stream源码分析.html"><strong aria-hidden="true">7.7.</strong> Rust Async: async-stream源码分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/走进Tokio的异步世界/走进Tokio的异步世界.html"><strong aria-hidden="true">7.8.</strong> 走进 Tokio 的异步世界</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/tokio.rs_runtime的实现/tokio.rs_runtime的实现.html"><strong aria-hidden="true">7.9.</strong> tokio.rs runtime 的实现</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Tokio_internals/Tokio_internals.html"><strong aria-hidden="true">7.10.</strong> Tokio internals</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Tokio_internals/译文：Tokio內部机制.html"><strong aria-hidden="true">7.11.</strong> 译：Tokio 内部机制</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust_Axum_HTTP_框架的架构分析/Rust_Axum_HTTP_框架的架构分析.html"><strong aria-hidden="true">7.12.</strong> Rust Axum HTTP 框架的架构分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/安利一个Rust_Game_Engine：Bevy--ECS部分/安利一个Rust_Game_Engine：Bevy--ECS部分.html"><strong aria-hidden="true">7.13.</strong> 安利一个Rust Game Engine：Bevy--ECS部分</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Tokio_解析之任务调度/Tokio_解析之任务调度.html"><strong aria-hidden="true">7.14.</strong> Tokio 解析之任务调度</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">8.</strong> 生态观察</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../11_生态观察/Rust_web_frameworks_have_subpar_error_reporting/Rust_web_frameworks_have_subpar_error_reporting.html"><strong aria-hidden="true">8.1.</strong> Rust web frameworks have subpar error reporting</a></li><li class="chapter-item expanded "><a href="../../11_生态观察/SeaORM：要做Rust版本的ActiveRecord/SeaORM：要做Rust版本的ActiveRecord.html"><strong aria-hidden="true">8.2.</strong> SeaORM：要做Rust版本的ActiveRecord</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">9.</strong> 死灵终极</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_总述.html"><strong aria-hidden="true">9.1.</strong> 译：Learn Rust the Dangerous Way 总述</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_0.html"><strong aria-hidden="true">9.2.</strong> 译：Learn Rust the Dangerous Way 0</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_1.html" class="active"><strong aria-hidden="true">9.3.</strong> 译：Learn Rust the Dangerous Way 1</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_2.html"><strong aria-hidden="true">9.4.</strong> 译：Learn Rust the Dangerous Way 2</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_3.html"><strong aria-hidden="true">9.5.</strong> 译：Learn Rust the Dangerous Way 3</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_4.html"><strong aria-hidden="true">9.6.</strong> 译：Learn Rust the Dangerous Way 4</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_5.html"><strong aria-hidden="true">9.7.</strong> 译：Learn Rust the Dangerous Way 5</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Unsafe_Rust_随堂小测/Unsafe_Rust_随堂小测（一）.html"><strong aria-hidden="true">9.8.</strong> Unsafe Rust 随堂小测（一）</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Unsafe_Rust_随堂小测/Unsafe_Rust_随堂小测（二）.html"><strong aria-hidden="true">9.9.</strong> Unsafe Rust 随堂小测（二）</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Unsafe_Rust_随堂小测/Unsafe_Rust_随堂小测（三）.html"><strong aria-hidden="true">9.10.</strong> Unsafe Rust 随堂小测（三）</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Unsafe_Rust_随堂小测/Unsafe_Rust_随堂小测参考答案.html"><strong aria-hidden="true">9.11.</strong> Unsafe Rust 随堂小测参考答案</a></li></ol></li></ol>
            </div>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
        </nav>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky bordered">
                    <div class="left-buttons">
                        <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </button>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                    </div>

                    <h1 class="menu-title">Rust 优秀博文</h1>

                    <div class="right-buttons">
                        <a href="../../print.html" title="Print this book" aria-label="Print this book">
                            <i id="print-button" class="fa fa-print"></i>
                        </a>

                    </div>
                </div>

                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>

                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script type="text/javascript">
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <h1 id="learn-rust-the-dangerous-way-系列文章翻译-1"><a class="header" href="#learn-rust-the-dangerous-way-系列文章翻译-1">Learn Rust the Dangerous Way-系列文章翻译-1</a></h1>
<p>译者：<a href="https://www.zhihu.com/people/cheng-zhao-5-83">cheng zhao</a></p>
<p>译文：<a href="https://zhuanlan.zhihu.com/p/686112754">https://zhuanlan.zhihu.com/p/686112754</a></p>
<p>原载：<a href="https://cliffle.com/p/dangerust/1">https://cliffle.com/p/dangerust/1</a></p>
<p>译文：</p>
<p>在这个部分，我们将使用一个粗糙的、优化的c程序并将其翻译成一个粗糙优化的unsafe的Rust程序。Rust程序和原始的C程序会获得相同的结果和相同的性能。</p>
<h2 id="我们的第一个程序"><a class="header" href="#我们的第一个程序">我们的第一个程序</a></h2>
<p>为了开始，我们需要一个C程序满足以下条件：</p>
<ul>
<li>足够短</li>
<li>独立的</li>
<li>使用一些C技巧进行了积极优化</li>
<li>但不是一个完整的toy</li>
</ul>
<p>我从 <a href="https://link.zhihu.com/?target=https%3A//benchmarksgame-team.pages.debian.net/benchmarksgame/">the Benchmarks Game</a> 选择了太阳系模拟(<a href="https://link.zhihu.com/?target=https%3A//benchmarksgame-team.pages.debian.net/benchmarksgame/program/nbody-gcc-8.html">nbody-gcc-8</a>)。这个游戏是一个很好的代码来源，其中的代码进行了极度的优化。这个特殊的程序使用SSE和内存布局/局部性优化来快速处理数字。</p>
<p>这就是C语言最擅长的事情。在撰写本文时，在该任务的排行榜上（<a href="https://link.zhihu.com/?target=https%3A//benchmarksgame-team.pages.debian.net/benchmarksgame/performance/nbody.html">the leaderboard for this task</a>），类c语言占主导地位，第二快的语言Go慢了整整3.5倍。</p>
<blockquote>
<p>类C语言的注解：
出于这个目的，我列举了没有垃圾收集器的语言，这些语言给了你对内存位置和诸如此类的重要控制，比如C、c++、Fortran、Ada，当然还有Rust。所有这些语言的程序版本的性能都在C版本的33%以内。</p>
</blockquote>
<p>为了防止Benchmarks Game链接发生改变，我做了一个本地备份</p>
<ul>
<li><a href="https://link.zhihu.com/?target=https%3A//cliffle.com/p/dangerust/1/nbody.gcc-8.c">nbody.gcc-8.c</a> - (<a href="https://link.zhihu.com/?target=https%3A//benchmarksgame-team.pages.debian.net/benchmarksgame/license.html">license</a>)</li>
</ul>
<p>我们将仔细检查源代码，在单独的选项卡或编辑器中打开一份副本可能会很有用。我偶尔会重新格式化C代码，以更好地适应狭窄的专栏。</p>
<h2 id="第一个翻译版本ugly的"><a class="header" href="#第一个翻译版本ugly的">第一个翻译版本(ugly的)</a></h2>
<p>让我们从C程序开始，并编写一个Rust程序来做同样的事情。要理解什么是“同样的事情”，我们需要仔细考虑C程序在表达什么。您可能会惊讶于哪几行代码占用了我们大部分时间!</p>
<p>(就人类语言而言，我们所做的更多的是音译或修饰，而不是真正的翻译，因为结果不会是流利的Rust。我们稍后会修复这个问题。)</p>
<h3 id="热身"><a class="header" href="#热身">热身</a></h3>
<p>C文件顶部的部分是一些库的引用，让我们先翻译这些内容做一个热身。Rust默认导入很多标准库;我们将引入额外的内存管理工具、SIMD内在特性和常量PI。</p>
<p>C：</p>
<pre><code class="language-c">#include &lt;stdint.h&gt;
#include &lt;stdalign.h&gt;
#include &lt;immintrin.h&gt;
#include &lt;math.h&gt;
#include &lt;stdio.h&gt;
</code></pre>
<p>Rust:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use std::mem;
use std::arch::x86_64::*;
use std::f64::consts::PI;
<span class="boring">}
</span></code></pre></pre>
<h3 id="类型"><a class="header" href="#类型">类型</a></h3>
<p>C程序定义了两个类型，<code>intnative_t</code>和<code>struct body</code>。通过学习程序，我们可以看到<code>intnative_t</code>仅在循环中被用作循环计数；直接跳到重点，它最终在Rust中根本不被使用，被usize取代。另一方面，<code>struct body</code>在各处都被使用。</p>
<p>C:</p>
<pre><code class="language-c">// intptr_t should be the native integer type on most 
// sane systems.
typedef intptr_t intnative_t;

typedef struct{
    double position[3], velocity[3], mass;
} body;
</code></pre>
<p>Rust:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// Note 1
#![allow(
    non_upper_case_globals,
    non_camel_case_types,
    non_snake_case,
)]

#[repr(C)]  // Note 2
struct body {
    position: [f64; 3],
    velocity: [f64; 3],
    mass: f64,
}
<span class="boring">}
</span></code></pre></pre>
<p>翻译中有两个Note需要解释一下：</p>
<p><strong>Note 1: allow是什么意思？</strong></p>
<p>像许多新出现的语言一样，Rust也有自己的命名约定。您将获得关于小写结构体名称之类的lint警告。为了避免一次更改所有内容，这个allow属性(类似于#pragma)通知编译器我们希望允许偏离命名约定。</p>
<p><strong>Note 2:</strong> <code>#[repr(C)]</code>标记，会应用于跟在后面的结构体定义上，要求Rust完全像C一样布局结构体。如果不加这个标记，Rust会假定你不太关注结构体中字段的顺序，Rust会优化它们以获得最佳的包装和对齐-这可能是你以前手工完成的。实际上，这个程序并没有假设一个特定的字段顺序，但是我们现在要关闭字段重新排序。</p>
<h3 id="数据"><a class="header" href="#数据">数据</a></h3>
<p>程序的下一个部分是展示了描述太阳系的数据。这是Rust和C非常相似的一个领域，所以我将利用这个机会来讨论它们不同之处背后的原则。总体主题是明确的。</p>
<p>C：</p>
<pre><code class="language-c">#define SOLAR_MASS (4*M_PI*M_PI)
#define DAYS_PER_YEAR 365.24
#define BODIES_COUNT 5


static body solar_Bodies[]={
    {    // Sun
        .mass=SOLAR_MASS


    },
    {    // Jupiter
        {
             4.84143144246472090e+00,
            -1.16032004402742839e+00,
            -1.03622044471123109e-01
        },
        {
             1.66007664274403694e-03 * DAYS_PER_YEAR,
             7.69901118419740425e-03 * DAYS_PER_YEAR,
            -6.90460016972063023e-05 * DAYS_PER_YEAR
        },
        9.54791938424326609e-04 * SOLAR_MASS
    },
    // ... more planets here ...
};
</code></pre>
<p>Rust：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// Note 1
const SOLAR_MASS: f64 = 4. * PI * PI;
const DAYS_PER_YEAR: f64 = 365.24;
const BODIES_COUNT: usize = 5;

//     ,------------------------------------- Note 2
//     |                           ,--------- Note 3
//     v                           v
static mut solar_Bodies: [body; BODIES_COUNT] = [
    body {    // Sun      &lt;------------------ Note 4
        mass: SOLAR_MASS,
        position: [0.; 3], // &lt;-------------- Note 5
        velocity: [0.; 3],
    },
    body {    // Jupiter
        position: [ // &lt;--------------------- Note 6
             4.84143144246472090e+00,
            -1.16032004402742839e+00,
            -1.03622044471123109e-01
        ],
        velocity: [
             1.66007664274403694e-03 * DAYS_PER_YEAR,
             7.69901118419740425e-03 * DAYS_PER_YEAR,
            -6.90460016972063023e-05 * DAYS_PER_YEAR
        ],
        mass: 9.54791938424326609e-04 * SOLAR_MASS
    },
    // ... more planets here ...
];
<span class="boring">}
</span></code></pre></pre>
<p><strong>Note 1:</strong> C语言中通过<code>#define</code> 定义的常量可以用Rust的<code>const</code>进行替代，<code>const</code>用于声明一个常量。常量有类型，这避免了一些令人尴尬的错误(不要问我是怎么知道的)。</p>
<p><strong>Note 2:</strong> 通常情况下，C中的数据是可以被重写（overwritten）的，除非该数据被<code>const</code>标识。Rust采取了相反的策略：数据是默认是不能被重写的 ，除非它被<code>mut</code>标识（mutable）。 (这个策略很好，因为编译器通常无法检测到您在C中意外地改变了某些内容，因为您忘记了<code>const</code>它。)</p>
<p><strong>Note 3:</strong> C在这里使用了一个隐含的数组大小。而在Rust中需要声明数组的大小。因为如果这个数组的大小偏离了BODIES_COUNT，在很多地方程序会出错，所以我觉得这是可以接受的。</p>
<p><strong>Note 4:</strong> 在C中编写结构字面值使用裸花括号<code>{…}</code>，并且类型必须从上下文中推断出来。在Rust中，必须显式声明类型:<code>body{…}</code></p>
<p><strong>Note 5:</strong> 在C中，对结构体进行初始化时可以省略一些字段—尽管不鼓励这样做。您需要为Rust中的每个字段都提供一些初始化项</p>
<p><strong>Note 6:</strong> 在C (C99之后)中，你可以选择在struct 初始化时给出字段名;我们的程序对太阳是这样做的，在Jupiter的初始化中则没有给出字段名，C语言会进行推断。在Rust中，必须总是给出字段名。(这意味着如果您重新排序结构中的字段，结构字面值不会改变含义!)</p>
<blockquote>
<p>省略字段的一个常见原因是，当您只关心结构体的一部分，并且不希望使用一长串零来使代码混乱时。别担心，Rust中有一种方法可以做到这一点。我在这里不介绍它，因为我认为它会分散注意力，但如果你想分散注意力，它叫struct update语法。</p>
</blockquote>
<h2 id="我们的第一个函数offset_momentum"><a class="header" href="#我们的第一个函数offset_momentum">我们的第一个函数：<code>offset_Momentum</code></a></h2>
<p>从这里开始，我们将无序地访问函数，因为这对我这个作者来说很方便。在Rust中，文件中函数的顺序并不重要，因此我们可以按照原始顺序或我使用的顺序转换程序。</p>
<p>让我们从这个最短的函数开始，<code>offset_Momentum</code>。 对于一个简短的函数，它会产生很多注释。</p>
<p>C：</p>
<pre><code class="language-c">static void offset_Momentum(body bodies[]){

    for(intnative_t i=0; i&lt;BODIES_COUNT; ++i)
        for(intnative_t m=0; m&lt;3; ++m)
            bodies[0].velocity[m]-=
              bodies[i].velocity[m]*bodies[i].mass/SOLAR_MASS;
}
</code></pre>
<p>Rust:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// v------------------------------------ Note 1
unsafe fn offset_Momentum(bodies: *mut body) {
// ^                               ^---- Note 2
    for i in 0..BODIES_COUNT {  // &lt;---- Note 3
        for m in 0..3 {
            (*bodies.add(0)).velocity[m] -=  // &lt;- Note 4
                (*bodies.add(i)).velocity[m] // &lt;- Note 5
                * (*bodies.add(i)).mass / SOLAR_MASS;
        }
    }
}
<span class="boring">}
</span></code></pre></pre>
<p><strong>Note 1:</strong> Rust版本的函数被标记为<code>unsafe</code>。 虽然这听起来像是一个可怕的词，但在我们的例子中，它只是意味着我们选择了使用指针进行任意(可能是愚蠢的)操作的能力，并且任何调用者都有可能传递虚假参数把当前的函数破坏掉。这和C程序提供的条件是一样的。</p>
<p><strong>Note 2:</strong> C程序接收一个<code>body bodies[]</code>参数。这个参数看起来像是一个数组，但它只是一个指针 - 该参数等价于<code>body *bodies</code>。因为该参数不是<code>const</code>，在Rust中等价为<code>*mut body</code>。</p>
<p><strong>Note 3:</strong> 循环的不同写法</p>
<p><strong>Note 4:</strong> 这是显而易见的：为什么C的表达式<code>bodies[0]</code>在Rust中写作<code>*bodies.add(0)</code> ？</p>
<p>首先我要向你保证这只是暂时的。我们将在本系列的下一部分中对其进行清理。</p>
<p>而现在：我这么做的原因是，在C程序中，</p>
<ol>
<li>我们使用了裸指针(C-style)</li>
<li>我们在做指针的算数运算（即C中的索引）</li>
<li>我们没有检查数组边界或NULL，因此如果调用者给了我们虚假的参数，我们所做的可能是错误的。</li>
</ol>
<p>Rust不会阻止我们这么做，但也不会特意帮助我们。通常，给定一个危险的选项和一个同样好的安全选项，Rust会试图通过使其更易于使用来引导您选择安全的选项。为了与显式的主题保持一致，做危险的事情需要更多的输入，所以很难偶然地做到。</p>
<p>在C中<code>bodies[i]</code>与<code>*(bodies + i)</code>是完全等价的 - 它实现了一个指针的算数运算和一个指针的解引用。特别是，它假设你有理由知道<code>i</code>是数组的有效索引。这是C语言中常见的情况，并且使用方括号来简化。</p>
<p>在Rust中 ，通常情况会使用引用<code>reference</code>来替代指针，并进行边界检查， 因此用例得到了简写的<code>bodies[i]</code>。执行未检查的指针算术是例外，而不是规则，因此我们必须更慎重地表达它。</p>
<p>最终我们不得不用<code>*bodies.add(i)</code>来替代<code>*(bodies + i)</code>，因为Rust不会重载指针的算术运算符。Rust中的指针提供了<code>add</code>,<code>sub</code>和一些其他的操作；这里有<a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/std/primitive.pointer.html%23methods">完整的操作列表</a> 。</p>
<blockquote>
<p>对机器来说，引用是指针，但对编译器和程序员来说，它们的含义略有不同。在本系列的下一部分中，我们将对此进行更深入的研究。</p>
</blockquote>
<p><strong>Note 5:</strong> 考虑到所有这些，您可能会惊讶地看到<code>velocity[m]</code>使用方括号。我选择化简它是因为我可以看到它是等价的:</p>
<ul>
<li><code>velocity</code>是结构体中的一个包含3个元素的数组</li>
<li>我们的索引是<code>0..3</code>(即下标是0,1,2)，因此每个放问都处于数组边界内</li>
</ul>
<p>如果我们试图用方括号读取<code>velocity</code>数组的末尾，程序将在运行时出现panic，这将是一个行为改变。既然我们不这么做，就不用担心了。</p>
<p>技术上，写<code>velocity[m]</code>要求编译器产生一个边界检查。而<code>rustc</code>非常擅长推断什么时候边界检查是不必要的，带有常量索引的简单循环是最简单的情况。我们将在后面看到更多这种模式。</p>
<h2 id="第二个函数output_energy"><a class="header" href="#第二个函数output_energy">第二个函数<code>output_Energy</code></a></h2>
<p>在大多数情况下，<code>output_Energy</code>应用了与<code>offset_Momentum</code>相同的技术，我将在此不作进一步评论。下面的注释提醒我们注意以前没有见过的事情。</p>
<p>C:</p>
<pre><code class="language-c">static void output_Energy(body bodies[]){
    double energy=0;
    for(intnative_t i=0; i&lt;BODIES_COUNT; ++i){
        // Add the kinetic energy for each body.
        energy+=0.5*bodies[i].mass*(
          bodies[i].velocity[0]*bodies[i].velocity[0]+
          bodies[i].velocity[1]*bodies[i].velocity[1]+
          bodies[i].velocity[2]*bodies[i].velocity[2]);

        // Add the potential energy between this body and
        // every other body.
        for(intnative_t j=i+1; j&lt;BODIES_COUNT; ++j){
            double position_Delta[3];

            for(intnative_t m=0; m&lt;3; ++m)
                position_Delta[m] =
                   bodies[i].position[m]
                      - bodies[j].position[m];

            energy-=bodies[i].mass*bodies[j].mass/sqrt(
              position_Delta[0]*position_Delta[0]+
              position_Delta[1]*position_Delta[1]+
              position_Delta[2]*position_Delta[2]);
        }
    }
    // Output the total energy of the system.
    printf(&quot;%.9f\n&quot;, energy);
}
</code></pre>
<p>Rust:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>unsafe fn output_Energy(bodies: *mut body){
    let mut energy = 0.;
    for i in 0..BODIES_COUNT {
        // Add the kinetic energy for each body.
        energy += 0.5 * (*bodies.add(i)).mass * (
            (*bodies.add(i)).velocity[0]
                * (*bodies.add(i)).velocity[0]
            + (*bodies.add(i)).velocity[1]
                * (*bodies.add(i)).velocity[1]
            + (*bodies.add(i)).velocity[2]
                * (*bodies.add(i)).velocity[2]);

        // Add the potential energy between this body and
        // every other body.
        for j in i+1..BODIES_COUNT {
            let mut position_Delta =   // &lt;----------- Note 1
                [mem::MaybeUninit::&lt;f64&gt;::uninit(); 3];
            for m in 0..3 {
                position_Delta[m].as_mut_ptr().write(
                    (*bodies.add(i)).position[m]
                        - (*bodies.add(j)).position[m]
                );
            }
            let position_Delta: [f64; 3] = // &lt;------- Note 2
                mem::transmute(position_Delta);

            energy -= (*bodies.add(i)).mass
                * (*bodies.add(j)).mass
                / f64::sqrt(               // &lt;------- Note 3
                      position_Delta[0]*position_Delta[0]+
                      position_Delta[1]*position_Delta[1]+
                      position_Delta[2]*position_Delta[2]
                  );
        }
    }

    // Output the total energy of the system.
    println!(&quot;{:.9}&quot;, energy);
}
<span class="boring">}
</span></code></pre></pre>
<p><strong>Note 1:</strong> 我们已经遇到C程序中的一些隐式的复杂性。</p>
<p>它声明一个局部变量</p>
<pre><code class="language-c">double position_Delta[3];
</code></pre>
<p>这是一个包含3个double数字的数组 - 但是最重要的部分是它缺少初始化。C不初始化局部变量，除非你给它们进行初始化。在这种情况下，<code>position_Delta</code>的元素包含任意的、不可预测的值。这没有关系，因为在接下来的循环中，程序会立即用好的值替换它们。</p>
<p>为什么它很重要呢？有时不初始化内存对性能很重要，因此我借此机会演示你将使用的技术。(在这个例子中，我测量了它，它似乎没有影响性能。)</p>
<p>未初始化的变量是C和许多其他语言中严重bug的根源。在Rust中可以有未初始化的变量，但必须输入更多。等效的代码是：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let mut position_Delta = [mem::MaybeUninit::&lt;f64&gt;::uninit(); 3];
// ...which is shorthand for...
let mut position_Delta: [mem::MaybeUninit&lt;f64&gt;; 3] =
    [mem::MaybeUninit::uninit(); 3];
<span class="boring">}
</span></code></pre></pre>
<p>（<code>::&lt;f64&gt;</code>表达式被称为&quot;turbofish&quot;。这里是我们唯一使用到的地方）</p>
<p>标准库提供<code>std::mem::MaybeUninit</code>表示可能未初始化的存储位置。<code>MaybeUninit&lt;T&gt;</code>表示<code>T</code>可能没有进行初始化。<code>MaybeUninit::uninit()</code>是一个未初始化的值。因此这一行声明了一个有着3个<code>f64</code>的数组，其中包含任意未初始化的内存，就像在C中一样——我们只需要写出这些话来向编译器保证，这就是我们想要的。</p>
<p>访问这些变量是<code>unsafe</code>的，但是<code>unsafe</code>不应该吓到你 - <code>unsafe</code>仅仅意味着你需要十分确认一件事情：在你读这些变量之前，这些变量进行了初始化。Rust代码做的第一件事，和C类似，都是使用有效的数字填充数组元素。 在Rust中填充数组要比C语言更冗长：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>position_Delta[m].as_mut_ptr().write(value_goes_here);
<span class="boring">}
</span></code></pre></pre>
<p>上面这句话什么意思呢？</p>
<p><code>MaybeUninit::as_mut_ptr()</code>产生了一个可变指针，该指针指向可能未初始化的内存。 <code>write</code>是另一个<a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/std/primitive.pointer.html%23methods">裸指针操作</a>， 该操作不先读取数据就将数据写入指向内存。</p>
<p>为什么写内存依赖于读内存呢？在Rust中，这通常是因为要存储的类型有析构函数。<code>f64</code>没有析构函数， 但我试图在这里保持学究式的正确，以防你把这段代码复制到另一个上下文中。</p>
<p><strong>Note 2:</strong> 这是我们的第一个<code>transmute</code>，下面给出代码，这样你就不用往前翻了：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let position_Delta: [f64; 3] = mem::transmute(position_Delta);
<span class="boring">}
</span></code></pre></pre>
<p><code>std::mem::transmute</code>是Rust中一个非常通用的函数。它在任意两种类型之间进行转换，只要它们的大小相同，而无需运行任何代码或更改任何数据——它只是将一种类型的位重新解释为另一种类型。它的功能和C中的类型强转类似，但是更容易预测也更强大 - 从技术上讲它相当于做这样的事情：</p>
<pre><code class="language-c">// we're in C here
float x = something();
int y = *(int *) &amp;x;
</code></pre>
<p>因为<code>transmute</code>会很高兴地将浮点数转换为指针，或者将任何三字结构体转换为其他任何结构体，所以它绝对是<code>unsafe</code>的。而且非常有用。</p>
<p>这里，我们把原始的<code>[MaybeUninit&lt;f64&gt;; 3]</code>（见Note 1）转为 <code>[f64; 3]</code> - 我们去掉了MaybeUninit，因为作为程序员的我们确信，此时数组已经完全初始化了。 这意味着我们可以在函数的其余部分自由访问数组元素。只要我们正确地完成了初始化，<code>transmute</code>的使用就是安全的。</p>
<p>请注意，我们在<code>=</code>右侧使用旧的position_Delta数组，并在<code>=</code>左侧创建一个具有相同名称的新position_Delta变量。这是一个Rust中常见的用于改变变量类型（或可见性）的习语。从C语言背景来看，这种“名称遮蔽(name shadowing)”可能对你来说很可怕，但没关系——你不必使用它。但你肯定会在Rust代码中遇到它。</p>
<p><strong>Note 3:</strong> 这个比较容易。C语言有sqrt和sqrtf, Rust有f64::sqrt和f32::sqrt。</p>
<h2 id="第三个函数advance"><a class="header" href="#第三个函数advance">第三个函数：<code>advance</code></a></h2>
<p><code>advance</code>是实际的模拟。 它使太阳系向前推进了一点点。与前面介绍过的两个函数相比，<code>advance</code>实在是太长了，因此我把它分成了几个部分。</p>
<p>原始的<code>advance</code>函数有着非常多的注释，这里由于篇幅的限制我去掉了它们。</p>
<h3 id="第一节-setup"><a class="header" href="#第一节-setup">第一节: setup</a></h3>
<p>这个问题很复杂，所以我将分别介绍这两种语言。</p>
<p>C：</p>
<pre><code class="language-c">static void advance(body bodies[]){

    #define INTERACTIONS_COUNT \
        (BODIES_COUNT*(BODIES_COUNT-1)/2)
    #define ROUNDED_INTERACTIONS_COUNT \
        (INTERACTIONS_COUNT+INTERACTIONS_COUNT%2)

    //  ,--------------------------------- Note 1
    // v       v-------------------------- Note 2
    static alignas(__m128d) double
      position_Deltas[3][ROUNDED_INTERACTIONS_COUNT],
      magnitudes[ROUNDED_INTERACTIONS_COUNT];
</code></pre>
<p>跳过这些<code>#define</code>，之前我们已经遇到过了。接下来请注意，这里是我第一次在C函数中添加Note。</p>
<p><strong>C Note 1:</strong> 这是一个在函数内部定义的<code>static</code>变量。 这使得该变量的作用域在该函数中，但不存储在堆栈中，而是放在全局内存中，这有很多含义。</p>
<ol>
<li>静态变量没有写初始化的语句，但它并不是未初始化，这一点与普通局部变量不同，它会被用0值进行初始化。</li>
<li>它被归零一次，所以这个函数不需要在每次被调用时初始化它。这样可以提高性能（与不初始化局部变量相同），这几乎肯定是本程序这样做的原因。</li>
<li>当然，它被初始化一次，那么只有在该函数首次被调用时，该变量确定是0。下一次被调用时，该变量的值是上一次被调用时的值。</li>
<li>作为一个副作用，该函数既不是可重入的，也不是线程安全的，因为对该函数的多个调用同时争夺静态存储将产生奇怪的结果。</li>
</ol>
<p><strong>C Note 2:</strong> <code>alignas</code>说明符是C11中的新功能，它将某些内容进行对齐。这里，我们要求以<code>__m128d</code>进行对齐，这是英特尔对包含两个双精度浮点数的SSE向量类型的名称。这最终会对齐到16字节的倍数。</p>
<p>当遇到这类说明符时，我们需要问两个问题:</p>
<ol>
<li>为什么要对齐?在这个程序中，因为我们把这些变量同时作为双精度和向量进行访问。</li>
<li>对齐的是什么?将<code>alignas(__m128d) double</code>读取为以16字节对齐的<code>double</code>很容易，但这是不正确的。alignas说明符应用于要声明的数组。这里我们有一个普通的<code>double</code>数组，但是有特殊的排列方式。</li>
</ol>
<blockquote>
<p>在我个人看来，使用联合更简单、更清晰、更不容易出错，但这程序不是我写的。</p>
</blockquote>
<p>Rust:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>unsafe fn advance(bodies: *mut body) {

    const INTERACTIONS_COUNT: usize =
        BODIES_COUNT * (BODIES_COUNT - 1) / 2;
    const ROUNDED_INTERACTIONS_COUNT: usize =
        INTERACTIONS_COUNT + INTERACTIONS_COUNT % 2;

    // Note 1
    #[repr(align(16))]
    #[derive(Copy, Clone)]
    struct Align16([f64; ROUNDED_INTERACTIONS_COUNT]);

    // Note 2
    static mut position_Deltas: [Align16; 3] =
        [Align16([0.; ROUNDED_INTERACTIONS_COUNT]); 3];
    static mut magnitudes: Align16 =
        Align16([0.; ROUNDED_INTERACTIONS_COUNT]);
<span class="boring">}
</span></code></pre></pre>
<p>我们从<strong>Note 1</strong>开始，一个神秘的结构出现了。</p>
<p>Rust有一个与C语言类似的对齐属性，但它只能应用于类型，而不能应用于值。在Rust中，我们没有重写<code>double</code>的对齐方式，而是创建了一个类似<code>AlignedDouble</code>的包装器。在这种情况下，因为我不是很有想象力，我把它叫做<code>Align16</code>。</p>
<p><code>Align16</code>包含一个长为<code>ROUNDED_INTERACTIONS_COUNT</code>，元素类型为<code>f64</code>的数组，并且以16字节对齐。 我把<code>Align16</code>写成了一个具有匿名字段的元组结构体，但如果您愿意，也可以将其编写为普通结构体。</p>
<p><code>Align16</code>被 <code>align</code>标记，同时也被 <code>#[derive(Copy, Clone)]</code>进行标记。 <code>#[derive(Copy, Clone)]</code>说明我们希望该类型可以自由复制，就像<code>f64</code>一样。我们需要它来编写<strong>Note 2</strong>中的数组初始化。</p>
<p>说到<strong>Note 2</strong>，这里我们使用数组字面量初始化静态量。静态值被显式地标记为<code>mut</code>，因为我们将修改它们。</p>
<p>与C语言相同，<code>mut</code>的部分呈现高级不可重入和非线程安全。Rust真的不喜欢这样，因为Rust提供了关于数据竞争的保证(也就是说，它们不存在)，而拥有这样一个<code>static mut</code>会让我们违反这种保证。但是我们不是在编写安全的Rust，所以我们有责任自己实现线程安全——在这个程序中，我们通过不使用线程来实现。</p>
<p>那么我们继续！</p>
<h3 id="第二节计算距离"><a class="header" href="#第二节计算距离">第二节：计算距离</a></h3>
<p>C语言这次使用了一个更复杂的<code>for</code>循环。 我没有缩进代码，记住我们是在函数内部。</p>
<p>C：</p>
<pre><code class="language-c">//                   v--------------------------------- Note 1
for(intnative_t i=0, k=0; i&lt;BODIES_COUNT-1; ++i)

    for(intnative_t j=i+1; j&lt;BODIES_COUNT; ++j, ++k) // Note 2
        for(intnative_t m=0; m&lt;3; ++m)
            position_Deltas[m][k]=
              bodies[i].position[m]-bodies[j].position[m];
</code></pre>
<p>Rust:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>{
    let mut k = 0;
    for i in 0..BODIES_COUNT-1 {
        for j in i+1..BODIES_COUNT {
            for m in 0..3 {
                position_Deltas[m].0[k] =
                    (*bodies.add(i)).position[m]
                      - (*bodies.add(j)).position[m];
            }
            k += 1;
        }
    }
}
<span class="boring">}
</span></code></pre></pre>
<p>仔细阅读C代码中Note 1和Note 2之间的代码。变量<code>k</code>在Note 1处进行声明和初始化，因此<code>k</code>的作用域在3个<code>for</code>循环，与<code>i</code>的作用域相同。但是在<code>k</code>Note 1没有自增 - 它在Note 2进行自增 。</p>
<p>所以<code>k</code>是我们进入<code>j</code>循环的次数。</p>
<p>C的for循环是一个微妙的工具，在C中很容易表达例子中的代码(尽管我们可以讨论结果的可读性如何)。 在Rust中也有一种惯用的方法来做这样的事情，但是它需要引入一些更高级的概念，这些概念现在还不合适。</p>
<p>相反，我把C循环拆开，只创建了一个<code>k</code>变量。外面的花括号限制了k的范围，就像在c中一样。有了这种简化，循环就变成了范围内的简单循环，(在我看来)更清楚的是，k到底在计数什么。</p>
<p>这通常(但不总是)是翻译复杂C for循环的正确方法——至少在第一次转写时是这样。</p>
<p>最后，关于Rust语法的两个简短说明:</p>
<ul>
<li>如果这是你第一次遇到像<code>position_Deltas[m].0[k]</code>,<code>.0</code>是访问元组结构体<code>Align16</code>中的第一个（也是唯一一个）匿名域， 这是我们在上一节中定义的。</li>
<li>你可能已经注意到我的Rust循环有右花括号，即使C没有。虽然这恰好是我在C语言中的个人偏好，但在Rust中使用右花括号，这不是个人偏好：Rust需要它。</li>
</ul>
<h3 id="第三节转为向量"><a class="header" href="#第三节转为向量">第三节：转为向量</a></h3>
<p>在我展示代码之前:如果你以前没有用C写过MMX或SSE代码，让我说明一下，<code>__m128d</code>是英特尔的(尴尬的)类型名称，该类型是两个<code>double</code>组成的的128位向量，而MMX/SSE操作以<code>_mm_</code>为前缀的函数出现。</p>
<p>继续:我们正在进入计算太阳系中每一对天体引力相互作用的循环。</p>
<p>C:</p>
<pre><code class="language-c">for(intnative_t i=0; i&lt;ROUNDED_INTERACTIONS_COUNT/2; ++i){
    __m128d position_Delta[3];

    for(intnative_t m=0; m&lt;3; ++m)
        position_Delta[m]=
            ((__m128d *)position_Deltas[m])[i];
</code></pre>
<p>Rust:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>for i in 0..ROUNDED_INTERACTIONS_COUNT/2 {
    let mut position_Delta =
        [mem::MaybeUninit::&lt;__m128d&gt;::uninit(); 3];
    for m in 0..3 {
        position_Delta[m].as_mut_ptr().write(
            *(&amp;position_Deltas[m].0
                as *const f64         // &lt;----- Note 1
                as *const __m128d).add(i)
        );
    }
    let position_Delta: [__m128d; 3] =
        mem::transmute(position_Delta);
<span class="boring">}
</span></code></pre></pre>
<p>这是我们开始将<code>double</code>数组作为SSE向量数组的地方，这样我们就可以一次两个地读取它们并对它们进行操作。</p>
<p>循环开始和结束部分的<code>position_Delta</code>和我们之前介绍过的“未初始化的数组”含义相同。程序创建了一个未初始化的<code>__m128</code>类型的数组，之后通过从<code>position_Deltas</code>中复制相关的值进行填充，加载两个<code>double/f64</code>值作为一个<code>__m128d</code>。在C中，我们通过把数组（即指针）类型强转为一个<code>__m128d *</code>类型，之后使用下标进行索引。</p>
<p>在Rust中我们做同样的事情。 Rust使用<code>as</code>强制转换基本类型。Rust比C代码多了一次类型转换（<strong>Note 1</strong>）。因为Rust中的表达式<code>&amp;x</code> 获得<code>x</code>的地址作为一个引用，但是我们想要的是一个裸指针 - 因此我们首先把它转成一个指针，之后再把这个指针转换成另一种类型。 我们将在本系列的下一部分中更详细地介绍引用。</p>
<p>（我在Rust中使用了<code>*const</code>尽管在C中使用的是一个可变指针，因为虽然C使用<code>non-const</code>指针，但是它也没有改变什么东西。也就是说，在C程序中缺少const可能是一个错误，尽管是无害的错误。）</p>
<h3 id="第四节向量数学运算"><a class="header" href="#第四节向量数学运算">第四节：向量数学运算</a></h3>
<p>为了在这一列中容纳更多的代码，我在这里取消了缩进，所以请记住，我们还在循环的中间。</p>
<p>C：</p>
<pre><code class="language-c">const __m128d distance_Squared=
  position_Delta[0]*position_Delta[0]+
  position_Delta[1]*position_Delta[1]+
  position_Delta[2]*position_Delta[2];

__m128d distance_Reciprocal=
   _mm_cvtps_pd(_mm_rsqrt_ps(_mm_cvtpd_ps(distance_Squared)));
for(intnative_t j=0; j&lt;2; ++j)
    distance_Reciprocal=distance_Reciprocal*1.5-
      0.5*distance_Squared*distance_Reciprocal*
      (distance_Reciprocal*distance_Reciprocal);

((__m128d *)magnitudes)[i] =
    0.01/distance_Squared*distance_Reciprocal;
</code></pre>
<p>Rust:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let distance_Squared: __m128d = _mm_add_pd(
    _mm_add_pd(
        _mm_mul_pd(position_Delta[0], position_Delta[0]),
        _mm_mul_pd(position_Delta[1], position_Delta[1]),
    ),
    _mm_mul_pd(position_Delta[2], position_Delta[2]),
);

let mut distance_Reciprocal: __m128d =
  _mm_cvtps_pd(_mm_rsqrt_ps(_mm_cvtpd_ps(distance_Squared)));
for _ in 0..2 {
    distance_Reciprocal = _mm_sub_pd(
        _mm_mul_pd(distance_Reciprocal, _mm_set1_pd(1.5)),
        _mm_mul_pd(
            _mm_mul_pd(
                _mm_mul_pd(
                    _mm_set1_pd(0.5),
                    distance_Squared,
                ),
                distance_Reciprocal,
            ),
            _mm_mul_pd(
                distance_Reciprocal,
                distance_Reciprocal,
            ),
        ));
}

(magnitudes.0.as_mut_ptr() as *mut __m128d)
    .add(i)
    .write(_mm_mul_pd(
        _mm_div_pd(
            _mm_set1_pd(0.01),
            distance_Squared,
        ),
        distance_Reciprocal,
    ));
<span class="boring">}
</span></code></pre></pre>
<p>这真的是好长啊!Rust程序包含很多对Intel MMX/SSE intrinsic的调用(以<code>_mm_</code>开始)。实际上，这两个程序之间唯一直接对应的行就是计算distance_Reciprocal初始值的行。实际上，这两个程序之间唯一直接对应的行就是计算<code>distance_Reciprocal</code>初始值的行。</p>
<p>这是有原因的:C程序在作弊。</p>
<p>好吧，这太苛刻了——C程序所做的是依赖于一个非标准的语言扩展，该扩展为某些类型提供操作符重载，包括__m128d。</p>
<p>在我写这篇文章的时候，Rust并没有在标准库中为__m128d提供操作符重载，所以我们显式地使用vector intrinsic。(与C语言不同的是，我实际上可以用更多的代码自己添加操作符重载，但我认为那样会分散注意力。)</p>
<h3 id="第五节结束"><a class="header" href="#第五节结束">第五节：结束</a></h3>
<p>这成了最大循环的主体;让我们关闭它并完成函数。这里不需要新技术，所以我将不加评论地介绍它。</p>
<p>C：</p>
<pre><code class="language-c">} // end of loop


for(intnative_t i=0, k=0; i&lt;BODIES_COUNT-1; ++i)

    for(intnative_t j=i+1; j&lt;BODIES_COUNT; ++j, ++k){
        const double
          i_mass_magnitude=bodies[i].mass*magnitudes[k],
          j_mass_magnitude=bodies[j].mass*magnitudes[k];

        for(intnative_t m=0; m&lt;3; ++m){
            bodies[i].velocity[m] -=
                position_Deltas[m][k]*j_mass_magnitude;
            bodies[j].velocity[m] +=
                position_Deltas[m][k]*i_mass_magnitude;
        }
    }

for(intnative_t i=0; i&lt;BODIES_COUNT; ++i)
    for(intnative_t m=0; m&lt;3; ++m)
        bodies[i].position[m]+=0.01*bodies[i].velocity[m];
</code></pre>
<p>Rust:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>} // end of loop

{
    let mut k = 0;
    for i in 0..BODIES_COUNT-1 {
        for j in i+1..BODIES_COUNT {
            let i_mass_magnitude =
                (*bodies.add(i)).mass * magnitudes.0[k];
            let j_mass_magnitude =
                (*bodies.add(j)).mass * magnitudes.0[k];
            for m in 0..3 {
                (*bodies.add(i)).velocity[m] -=
                   position_Deltas[m].0[k] * j_mass_magnitude;
                (*bodies.add(j)).velocity[m] +=
                   position_Deltas[m].0[k] * i_mass_magnitude;
            }
            k += 1;
        }
    }
}

for i in 0..BODIES_COUNT {
    for m in 0..3 {
        (*bodies.add(i)).position[m] +=
            0.01 * (*bodies.add(i)).velocity[m];
    }
}
<span class="boring">}
</span></code></pre></pre>
<p>到这里<code>advance</code>函数就结束了。让我们继续吧！</p>
<h2 id="最后一个函数main"><a class="header" href="#最后一个函数main">最后一个函数：<code>main</code></a></h2>
<p>main函数只是简单的调用了我们上面介绍过的函数。</p>
<p>C：</p>
<pre><code class="language-c">int main(int argc, char *argv[]){

    offset_Momentum(solar_Bodies);
    output_Energy(solar_Bodies);
    for(intnative_t n=atoi(argv[1]); n--;
        advance(solar_Bodies));

    output_Energy(solar_Bodies);

}
</code></pre>
<p>Rust:</p>
<pre><pre class="playground"><code class="language-rust">fn main() {
    unsafe {
        offset_Momentum(solar_Bodies.as_mut_ptr());  // Note 1
        output_Energy(solar_Bodies.as_mut_ptr());
        let c = std::env::args().nth(1).unwrap()  // Note 2
            .parse().unwrap();
        for _ in 0..c {
            advance(solar_Bodies.as_mut_ptr())
        }
        output_Energy(solar_Bodies.as_mut_ptr());
    }
}
</code></pre></pre>
<p>这两种实现很相似，但还有一些不同。</p>
<p><strong>Note 1:</strong> 大家应该记得<code>solar_Bodies</code>是上面<code>定义的body</code>结构体的静态可变数组 。在C中我们把它传递给函数仅通过数组名，即<code>solar_Bodies</code>。这是可以工作的，是因为在C中我们不能通过<strong>值传递</strong>传递数组，因此使用数组名会衰变为指针。在C语言中，下面的程序</p>
<pre><code class="language-c">int data[5];

foo(data);
</code></pre>
<p>等价于：</p>
<pre><code class="language-c">int data[5];

foo(&amp;data[0]);
</code></pre>
<p>而在Rust中则不是这样的。如果我们把<code>solar_Bodies</code>传递给一个函数，则是<strong>值传递</strong>，这并不是我们想要的。</p>
<p>和C类似，Rust中的&amp;符号可以用来获取变量的地址，但是<code>&amp;solar_Bodies</code>产生一个引用而非指针。我们需要把它转成一个指针。</p>
<p>然而 ，这种方式太普通了，这里有另一种更短的写法：</p>
<p><code>array.as_mut_ptr()</code>返回一个指向数组第一个元素的<code>mut</code>指针。我们使用这个方法把<code>solar_Bodies</code> 传递给每一个操作它的函数。</p>
<p>Note 2: 这两个程序都将第一个命令行参数解析为整数，并使用它来确定<code>advance</code>执行的次数。这两个程序的处理方式不同。</p>
<p>在C中我们使用：</p>
<pre><code class="language-c">atoi(argv[1])
</code></pre>
<p>在Rust中，我们用一个更长的表达式：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>std::env::args().nth(1).unwrap()
    .parse().unwrap()
<span class="boring">}
</span></code></pre></pre>
<p>这是另一个C语言的隐式复杂性在Rust中变得显式的例子，这一切都归结为失败行为。</p>
<p>在没有提供命令行参数时，C语言会怎么做呢？<code>argv[1]</code>就会跳过<code>argv</code>数组的结尾，因此它以char *的形式读取任意内存。然后将任意指针传递给<code>atoi</code>，它读取它所指向的内存并尝试将其解析为整数。实际上，至少在Linux上，这是段错误。</p>
<p>如果传递了一个不是整数的参数，C语言会怎么做呢？<code>atoi</code>不检测错误，因此非整数参数被视为0。</p>
<p>Rust程序包含<code>unwrap()</code>的调用，这是一种Rust习惯用法，将错误条件转换为立即panic。我们可以选择一个不同的响应，比如用默认值替换无效参数，但是对于这个程序，我没有这么做。</p>
<p>在段程序中是我引入了内存安全，但原始程序没有内存安全 -- 为什么是现在呢？出于跨平台兼容性的原因，Rust将命令行参数封装在抽象之后，这使得很难读取argv的结尾。与其让我头痛，不如选择Rust的方式。</p>
<h2 id="评价翻译后的程序"><a class="header" href="#评价翻译后的程序">评价翻译后的程序</a></h2>
<p><a href="https://link.zhihu.com/?target=https%3A//cliffle.com/p/dangerust/1/nbody-1.rs">这里给出了翻译后的完整代码</a></p>
<p>构建C版本代码：</p>
<pre><code class="language-bash">$ time gcc -O3 -fomit-frame-pointer -march=native -funroll-loops -static \
    nbody.gcc-8.c -o nbody.gcc-8 -lm

real	0m0.294s
user	0m0.264s
sys	0m0.030s
</code></pre>
<p>构建Rust版本代码：</p>
<pre><code class="language-bash">$ time rustc -C opt-level=3 -C target-cpu=native -C codegen-units=1 \
    nbody-1.rs -o nbody-1

real	0m0.369s
user	0m0.310s
sys	0m0.048s
</code></pre>
<p>这里<code>rustc</code>的编译比<code>gcc</code>要略慢一些，但是他们都足够快，我们看到的不同更多的可能是启动时间。为了进行更公平的比较，我将包括使用clang构建结果，clang与rustc共享大量代码。</p>
<pre><code class="language-bash">$ time clang -O3 -fomit-frame-pointer -march=native -funroll-loops -static \
    1/nbody.gcc-8.c -o nbody.clang-8.bench -lm

real	0m0.418s
user	0m0.343s
sys	0m0.058s
</code></pre>
<p>让我们先看一下C版本的输出，以确认我们翻译的程序是正确的：</p>
<pre><code class="language-bash">$ ./nbody.gcc-8 50000000
-0.169075164
-0.169059907
</code></pre>
<p>Rust版本的输出如下：</p>
<pre><code class="language-bash">$ ./nbody-1 50000000
-0.169075164
-0.169059907
</code></pre>
<p>输出是匹配的(这主要意味着我们没有弄乱浮点运算的顺序)。我们可以使用<a href="https://link.zhihu.com/?target=https%3A//github.com/sharkdp/hyperfine">hyperfine</a>来比较性能。在本例中，与本系列中介绍的所有性能测量一样，我使用的是Intel i7-8550U，关闭了性能调节器和Turbo Boost。</p>
<div class="table-wrapper"><table><thead><tr><th>Command</th><th>Mean [s]</th><th>Min [s]</th><th>Max [s]</th><th>Ratio</th></tr></thead><tbody>
<tr><td>./nbody.gcc-8.bench 50000000</td><td>6.199 ± 0.006</td><td>6.185</td><td>6.206</td><td>1.17x</td></tr>
<tr><td>./nbody.clang-8.bench 50000000</td><td>5.277 ± 0.007</td><td>5.258</td><td>5.282</td><td>1.00x</td></tr>
<tr><td>./nbody-1.bench 50000000</td><td>5.123 ± 0.024</td><td>5.095</td><td>5.161</td><td>0.97x</td></tr>
</tbody></table>
</div>
<p>我们最初的粗糙的Rust程序与使用clang编译的C程序的速度大致相同。由于gcc要慢得多，因此在本系列的其余部分中，我将使用clang作为比较点。</p>
<h2 id="下一步"><a class="header" href="#下一步">下一步</a></h2>
<p>我们已经使用了一个C程序并生成了一个本质上等价的Rust程序，包括指针强制转换和未初始化的变量。结果是一个Rust和C程序员都不会喜欢的程序，但它表明了一个重要的观点:我们可以在Rust中表达和C中一样东西，只是语法不同而已。Rust希望帮助您保持安全，但与许多“安全”语言不同的是，它会在需要时为您让路。</p>
<p>这两个程序在大致相同的时间内得到相同的结果。在<a href="https://zhuanlan.zhihu.com/p/686395575">第2部分</a>中，我将开始清理代码，并讨论使用safe Rust特性生成更健壮代码的优点，以及安全性的性能成本。(剧透:我们不会再慢了。)</p>
<p>编辑于 2024-03-11 16:58・IP 属地北京</p>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->
                            <a rel="prev" href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_0.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                                <i class="fa fa-angle-left"></i>
                            </a>

                            <a rel="next" href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_2.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                                <i class="fa fa-angle-right"></i>
                            </a>

                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">
                    <a rel="prev" href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_0.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>

                    <a rel="next" href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_2.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                        <i class="fa fa-angle-right"></i>
                    </a>
            </nav>

        </div>




        <script type="text/javascript">
            window.playground_copyable = true;
        </script>


        <script src="../../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../../mark.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../../searcher.js" type="text/javascript" charset="utf-8"></script>

        <script src="../../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../../highlight.js" type="text/javascript" charset="utf-8"></script>
        <script src="../../book.js" type="text/javascript" charset="utf-8"></script>

        <!-- Custom JS scripts -->


    </body>
</html>
